/// ------------------------------------------------------------------------------------------------------------------------------------
///
/// MIT License
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to deal
/// in the Software without restriction, including without limitation the rights
/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
/// copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in all
/// copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
/// SOFTWARE.
///
/// Copyright (c) 2024 ycz. All rights reserved.
///
/// Created by ycz on 2024/3/20.
///
/// @file  y_ll.c
///
/// @brief
///     y_ll 是对嵌入式芯片平台底层接口的封装  low interface
///
/// ------------------------------------------------------------------------------------------------------------------------------------



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 头文件
/// ------------------------------------------------------------------------------------------------------------------------------------

#include "y_ll.h"

#include <string.h>
#include <malloc.h>
#include "y_log.h"
#include "rtc.h"



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 宏定义
/// ------------------------------------------------------------------------------------------------------------------------------------

#define ADDR_FLASH_SECTOR_0  ((uint32_t) 0x08000000)  ///< 扇区0起始地址, 16 Kbytes
#define ADDR_FLASH_SECTOR_1  ((uint32_t) 0x08004000)  ///< 扇区1起始地址, 16 Kbytes
#define ADDR_FLASH_SECTOR_2  ((uint32_t) 0x08008000)  ///< 扇区2起始地址, 16 Kbytes
#define ADDR_FLASH_SECTOR_3  ((uint32_t) 0x0800C000)  ///< 扇区3起始地址, 16 Kbytes
#define ADDR_FLASH_SECTOR_4  ((uint32_t) 0x08010000)  ///< 扇区4起始地址, 64 Kbytes
#define ADDR_FLASH_SECTOR_5  ((uint32_t) 0x08020000)  ///< 扇区5起始地址, 128 Kbytes
#define ADDR_FLASH_SECTOR_6  ((uint32_t) 0x08040000)  ///< 扇区6起始地址, 128 Kbytes
#define ADDR_FLASH_SECTOR_7  ((uint32_t) 0x08060000)  ///< 扇区7起始地址, 128 Kbytes
#define ADDR_FLASH_SECTOR_8  ((uint32_t) 0x08080000)  ///< 扇区8起始地址, 128 Kbytes
#define ADDR_FLASH_SECTOR_9  ((uint32_t) 0x080A0000)  ///< 扇区9起始地址, 128 Kbytes
#define ADDR_FLASH_SECTOR_10 ((uint32_t) 0x080C0000)  ///< 扇区10起始地址,128 Kbytes
#define ADDR_FLASH_SECTOR_11 ((uint32_t) 0x080E0000)  ///< 扇区11起始地址,128 Kbytes



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 全局变量
/// ------------------------------------------------------------------------------------------------------------------------------------

static size_t g_mem_use_size;    ///< 内存使用大小
static size_t g_mem_malloc_num;  ///< malloc 次数
static size_t g_mem_free_num;    ///< free 次数



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 私有函数
/// ------------------------------------------------------------------------------------------------------------------------------------

/// @brief   获取当前扇区索引
/// @param   [in] addr                     地址
/// @return  扇区索引
static uint16_t _y_nvs_get_sector(uint32_t addr) {
    if (addr < ADDR_FLASH_SECTOR_1)
        return 0;
    else if (addr < ADDR_FLASH_SECTOR_2)
        return 1;
    else if (addr < ADDR_FLASH_SECTOR_3)
        return 2;
    else if (addr < ADDR_FLASH_SECTOR_4)
        return 3;
    else if (addr < ADDR_FLASH_SECTOR_5)
        return 4;
    else if (addr < ADDR_FLASH_SECTOR_6)
        return 5;
    else if (addr < ADDR_FLASH_SECTOR_7)
        return 6;
    else if (addr < ADDR_FLASH_SECTOR_8)
        return 7;
    else if (addr < ADDR_FLASH_SECTOR_9)
        return 8;
    else if (addr < ADDR_FLASH_SECTOR_10)
        return 9;
    else if (addr < ADDR_FLASH_SECTOR_11)
        return 10;
    return 11;
}



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 公有函数
/// ------------------------------------------------------------------------------------------------------------------------------------

/// @brief   打印 y_ll 版本信息
void y_ll_print_version() {
    YLOG_VERSION("y_ll", Y_LL_MAJOR, Y_LL_MINOR, Y_LL_PATCH);
}



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 公有函数 -- 系统部分
/// ------------------------------------------------------------------------------------------------------------------------------------

/// @brief   打印内存使用信息
void y_ll_print_mem_info() {
    YLOGI("mem_use %d   malloc_num : %d  free_num : %d", g_mem_use_size, g_mem_malloc_num, g_mem_free_num);
}

/// @brief   获取已使用的内存大小
/// @return  内存大小
size_t y_ll_get_use_mem() {
    return g_mem_use_size;
}

/// @brief   动态分配内存
/// @param   [in] size                     内存大小
/// @return  指针地址
void *y_ll_malloc(size_t size) {

    void *p = NULL;
    if (size) {
        p = malloc(size);
        if (p) {
            // g_mem_use_size += size; // windows
            g_mem_use_size += (size % sizeof(int)) ? (size / sizeof(int) * sizeof(int)) + sizeof(int) : size;  // stm32 有余数 则需要字节对齐
            g_mem_malloc_num++;
        } else {
            YLOGW("y_ll_malloc %d error", size);
        }
    }

    return p;
}

/// @brief   释放内存
/// @param   [in] p                         指针地址
void y_ll_free(void *p) {
    if (p) {
        // g_mem_use_size -= _msize(p);                           // windows
        g_mem_use_size -= malloc_usable_size(p) - sizeof(int);  // stm32
        g_mem_free_num++;
        free(p);
        p = NULL;
    }
}

/// @brief   跳到指定地址运行
/// @param   [in] addr                     地址
void y_ll_jump_addr(uint32_t addr) {
    // todo
}

/// @brief   系统重启
void y_ll_reboot() {
    HAL_NVIC_SystemReset();
}

/// @brief   系统延时 ms
/// @param   [in] ms                       延时时间
void y_ll_delay(uint32_t ms) {
    if (ms) {
        HAL_Delay(ms);
    }
}

/// @brief   获取 硬件 版本号
/// @return  硬件版本号
VERSION_st y_ll_get_hardware_version() {

    // 读取 GPIO 确定硬件版本号(若支持)
    VERSION_st hw_ver = {0};
    hw_ver.major      = 0;
    hw_ver.minor      = 1;
    hw_ver.patch      = 0;

    return hw_ver;
}

/// @brief   获取 mac 地址
/// @retval  true                          成功
/// @retval  false                         失败
uint8_t *y_ll_get_mac() {

    // 获取芯片唯一 id stm32
    static uint8_t mac[6] = {0};
    uint8_t        id[15] = {0};
    uint32_t       id0    = HAL_GetUIDw0();
    uint32_t       id1    = HAL_GetUIDw1();
    uint32_t       id2    = HAL_GetUIDw2();
    memcpy(&id[0], (uint8_t *) &id0, 4);
    memcpy(&id[4], (uint8_t *) &id1, 4);
    memcpy(&id[8], (uint8_t *) &id2, 4);

    // 获取 id[12] id[13]
    for (int i = 0; i < 12; i++) {
        id[12] += id[i];
        id[13] ^= id[i];
    }

    // 获取 cpu id
    uint32_t cpuid = (id0 >> 1) + (id1 >> 2) + (id2 >> 3);

    // 生成 mac
    memcpy(mac, &cpuid, sizeof(uint32_t));
    mac[4] = id[12];
    mac[5] = id[13];

    // 打印 mac
    YLOGI(MAC_FORMAT, MAC_TO_STR(mac));

    return mac;
}



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 公有函数 -- flash 部分
/// ------------------------------------------------------------------------------------------------------------------------------------

/// @brief   擦除
/// @param   [in] addr                     地址
/// @param   [in] size                     大小
/// @retval  true                          成功
/// @retval  false                         失败
bool y_ll_flash_erase(uint32_t addr, uint32_t size) {

    // 尝试 3 次
    for (int x = 0; x < 3; ++x) {
        HAL_FLASH_Unlock();  // 解锁
        FLASH_EraseInitTypeDef flash_erase = {0};
        flash_erase.TypeErase              = FLASH_TYPEERASE_SECTORS;  // 扇区擦除
        flash_erase.Sector                 = _y_nvs_get_sector(addr);  // 扇区编号
        flash_erase.NbSectors              = 1;                        // 要擦除的页数
        flash_erase.VoltageRange           = FLASH_VOLTAGE_RANGE_3;    // 要擦除的页数
        uint32_t PageError                 = 0;                        // 错误信息
        if (HAL_FLASHEx_Erase(&flash_erase, &PageError) == HAL_OK) {   // 擦除
            HAL_FLASH_Lock();                                          // 上锁
            return true;
        }
        HAL_FLASH_Lock();  // 上锁
    }

    return false;
}

/// @brief   读取
/// @param   [in]  addr                    地址
/// @param   [out] buf                     数据
/// @param   [in]  size                    大小
/// @return  读取成功的大小
uint32_t y_ll_flash_read(uint32_t addr, uint8_t *buf, uint32_t size) {

    for (uint32_t i = 0; i < size; i++) {
        buf[i] = *(__IO uint8_t *) (addr + i);
    }
    return size;
}

/// @brief   写入
/// @param   [in]  addr                    地址
/// @param   [in]  buf                     数据
/// @param   [in]  size                    大小
/// @return  写入成功的大小
uint32_t y_ll_flash_write(uint32_t addr, uint8_t *buf, uint32_t size) {

    HAL_FLASH_Unlock();  // 解锁
    uint32_t offset = size;
    do {
        if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr + size - offset, *(uint32_t *) (buf + size - offset)) != HAL_OK) {
            HAL_FLASH_Lock();  // 上锁
            continue;
        }
        if (offset >= 4) {
            offset -= 4;
        } else {
            offset = 0;
        }
    } while (offset);
    HAL_FLASH_Lock();  // 上锁

    return size;
}



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 公有函数 -- 时间部分
/// ------------------------------------------------------------------------------------------------------------------------------------

/// @brief   获取 rtc 时间
/// @return  时间指针
TIME_st y_ll_time_get() {

    TIME_st         time     = {0};
    RTC_DateTypeDef rtc_date = {0};
    RTC_TimeTypeDef rtc_time = {0};

    // 必须同时调用 且先获取时间 再获取日期
    HAL_RTC_GetTime(&hrtc, &rtc_time, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(&hrtc, &rtc_date, RTC_FORMAT_BIN);
    time.year = rtc_date.Year + 2000;
    time.mon  = rtc_date.Month;
    time.day  = rtc_date.Date;
    time.hour = rtc_time.Hours;
    time.min  = rtc_time.Minutes;
    time.sec  = rtc_time.Seconds;
    return time;
}

/// @brief   设置 rtc 时间
/// @param   [in] time                     要设置的时间
/// @retval  true                          成功
/// @retval  false                         失败
bool y_ll_time_set(TIME_st *time) {

    // 断言
    YLOGA_FALSE(time);

    RTC_TimeTypeDef rtc_time = {0};
    RTC_DateTypeDef rtc_date = {0};
    rtc_date.Year            = time->year - 2000;
    rtc_date.Month           = time->mon;
    rtc_date.Date            = time->day;
    rtc_time.Hours           = time->hour;
    rtc_time.Minutes         = time->min;
    rtc_time.Seconds         = time->sec;
    // 先设置时间 再设置日期
    if (HAL_RTC_SetTime(&hrtc, &rtc_time, RTC_FORMAT_BIN) != HAL_OK || HAL_RTC_SetDate(&hrtc, &rtc_date, RTC_FORMAT_BIN) != HAL_OK) {
        YLOGD("set rtc error");
        return false;
    }

    return true;
}